﻿using System;
using System.Collections.Generic;

namespace TCPActivePull.Service
{
    public static class ModbusHelper
    {
        /// <summary>
        /// 创建Modbus读取请求
        /// </summary>
        /// <param name="functionCode">功能码：0x03（读保持寄存器）或0x04（读输入寄存器）</param>
        public static byte[] CreateReadRequest(ushort transactionId, byte unitId,
            ushort startAddress, ushort registerCount, byte functionCode = 0x03)
        {
            // 验证功能码（0x03：保持寄存器，0x04：输入寄存器）
            if (functionCode != 0x03 && functionCode != 0x04)
            {
                throw new ArgumentException("不支持的功能码，仅支持0x03和0x04", nameof(functionCode));
            }

            // Modbus TCP帧结构:
            // 事务处理标识 (2字节) + 协议标识 (2字节，0x0000) + 长度 (2字节) + 
            // 单元标识 (1字节) + 功能码 (1字节) + 起始地址 (2字节) + 寄存器数量 (2字节)

            byte[] request = new byte[12];

            // 事务处理标识
            request[0] = (byte)(transactionId >> 8);
            request[1] = (byte)(transactionId & 0xFF);

            // 协议标识 (0 = Modbus)
            request[2] = 0x00;
            request[3] = 0x00;

            // 长度 (后面的字节数: 1 + 1 + 2 + 2 = 6)
            request[4] = 0x00;
            request[5] = 0x06;

            // 单元标识
            request[6] = unitId;

            // 功能码
            request[7] = functionCode;

            // 起始地址
            request[8] = (byte)(startAddress >> 8);
            request[9] = (byte)(startAddress & 0xFF);

            // 寄存器数量
            request[10] = (byte)(registerCount >> 8);
            request[11] = (byte)(registerCount & 0xFF);

            return request;
        }

        /// <summary>
        /// 解析Modbus响应
        /// </summary>
        public static (bool Success, List<short> Values, string ErrorMessage) ParseResponse(byte[] response)
        {
            try
            {
                // 验证响应长度至少为8字节（最小响应长度）
                if (response == null || response.Length < 8)
                {
                    return (false, null, $"响应长度无效：{response?.Length ?? 0}字节（至少需要8字节）");
                }

                // 检查功能码是否包含错误标志
                if ((response[7] & 0x80) == 0x80)
                {
                    // 错误响应
                    byte errorCode = response.Length > 8 ? response[8] : (byte)0xFF;
                    string errorDesc = GetModbusErrorCodeDesc(errorCode);
                    return (false, null, $"设备返回错误，功能码: 0x{response[7]:X2}, 错误码: 0x{errorCode:X2}（{errorDesc}）");
                }

                // 验证功能码是否为0x03或0x04
                if (response[7] != 0x03 && response[7] != 0x04)
                {
                    return (false, null, $"不支持的功能码: 0x{response[7]:X2}（仅支持0x03和0x04）");
                }

                // 数据长度
                byte dataLength = response[8];

                // 验证总长度是否匹配
                if (response.Length != 9 + dataLength)
                {
                    return (false, null, $"响应长度不匹配：实际{response.Length}字节，预期{9 + dataLength}字节");
                }

                // 解析寄存器值（0x03和0x04都是16位寄存器，处理逻辑相同）
                var values = new List<short>();

                if (dataLength % 2 != 0)
                {
                    return (false, null, $"数据长度无效：{dataLength}字节（应为偶数，16位寄存器）");
                }

                int registerCount = dataLength / 2;
                for (int i = 0; i < registerCount; i++)
                {
                    int index = 9 + i * 2;
                    if (index + 1 >= response.Length) break;

                    // 高位字节在前（大端模式）
                    short value = (short)((response[index] << 8) | response[index + 1]);
                    values.Add(value);
                }

                return (true, values, null);
            }
            catch (Exception ex)
            {
                return (false, null, $"解析响应异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 获取Modbus错误码描述
        /// </summary>
        private static string GetModbusErrorCodeDesc(byte errorCode)
        {
            return errorCode switch
            {
                0x01 => "非法功能（设备不支持该功能码）",
                0x02 => "非法数据地址（寄存器地址不存在）",
                0x03 => "非法数据值（如寄存器数量超上限）",
                0x04 => "从站设备故障（设备内部错误）",
                0x05 => "确认（仅写操作）",
                0x06 => "从站设备忙（处理其他请求中）",
                0x08 => "内存奇偶校验错误",
                0x0A => "网关路径不可用",
                0x0B => "网关目标设备未响应",
                _ => "未知错误"
            };
        }
    }
}
